#include "./handler_task.h"
#include "../driver/cmd_uart.h"
#include "../driver/door_motor.h"
#include "../driver/nozzle_motor.h"
#include "../driver/parts.h"
#include "./draw_task.h"
#include "./heat_task.h"
#include "../service/work_service.h"
#include "../service/config.h"
#include "./rf_task.h"
#include "../service/ota_service.h"

namespace task {

using namespace utils;
using namespace conn;
using namespace driver;
using namespace service;

static Error on_set_door_adc(const Message &msg) {
    uint16_t arg;
    Error err = msg.parse_req(arg);
    if (err) {
        goto finish;
    }
    config->door_lock_adc = arg;
    err = config.save();
finish:
    return err;
}

static Error on_set_pump(const Message &msg) {
    uint16_t arg;
    Error err = msg.parse_req(arg);
    if (err) {
        goto finish;
    }
    config->pump_ml_ms = arg;
    err = config.save();
finish:
    return err;
}

static void on_rf_translate(const Message &msg) {
    uint8_t cmd;
    utils::ByteView bw;
    Frame result;
    Error err = msg.parse_req(cmd, bw);
    if (err) {
        goto finish;
    }
    err = rfTask->send(result, cmd, bw);
finish:
    if (err) {
        cmdUart->write_resp(msg.seq(), msg.req_cmd(), err);
    } else {
        utils::ByteView buf;
        buf.buf = result.data;
        buf.len = result.data_len;
        cmdUart->write_resp(msg.seq(), msg.req_cmd(), err, 
            result.cmd,
            buf
        );
    }
}

static Error on_clean(const Message &msg) {
    CleanParam param;
    Error err = msg.parse_req(
        param.c,
        param.drain_cold_water,
        param.d,
        param.steam_duration
    );
    if (err) {
        goto finish;
    }
    err = clean(param);
finish:
    return err;
}

static Error on_cooking(const Message &msg) {
    CookingParam param;
    Error err = msg.parse_req(
        param.k,
        param.pre_water,
        param.f,
        param.g,
        param.defroze_duration,
        param.e,
        param.pour_water,
        param.h,
        param.mix_soup_duration,
        param.i,
        param.clogging_prevent_duration
    );
    err = cooking(param);
finish:
    return err;
}

static Error on_set_flow_arg(const Message &msg) {
    uint16_t count;
    Error err = msg.parse_req(count);
    if (err) {
        goto finish;
    }
    config->flow1000_count = count;
    err = config.save();
finish:
    return err;
}

static Error on_set_steam_heat_arg(const Message &msg) {
    uint8_t m;
    uint16_t temp, kpa;
    Error err = msg.parse_req(m, temp, kpa);
    if (err) {
        goto finish;
    }
    config->heat_timeout = m * 60 * 1000;
    config->steam_temp = temp;
    config->steam_kpa = kpa;
    err = config.save();
finish:
    return err;
}

static Error on_set_steam_draw_timeout(const Message &msg) {
    uint8_t m;
    Error err = msg.parse_req(m);
    if (err) {
        goto finish;
    }
    config->draw_timeout = m * 60 * 1000;
    err = config.save();
finish:
    return err;
}

static Error on_draw_water_test(const Message &msg) {
    uint8_t ml;
    Error err = msg.parse_req(ml);
    if (err) {
        goto finish;
    }
    err = draw_water(ml);
finish:
    return err;
}

static Error on_draw_heat_ctrl(const Message &msg) {
    uint8_t ctrl;
    Error err = msg.parse_req(ctrl);
    if (err) {
        goto finish;
    }
    switch (ctrl) {
        case 0: drawTask->reset(); break;
        case 1: drawTask->stop(); break;
        case 2: heatTask->reset(); break;
        case 3: heatTask->stop(); break;
        
        default: err = ec::INVALID; break;
    }
finish:
    return err;
}

static Error on_parts_ctrl(const Message &msg) {
    uint8_t id, val;
    uint16_t value;
    Error err = msg.parse_req(id, val);
    if (err) {
        goto finish;
    }
    value = (id << 8) + val;
    switch (value) {
        case 0x0100: parts->steam_heat_open(); break;
        case 0x0101: parts->steam_heat_close(); break;

        case 0x0200: parts->water_heat_open(); break;
        case 0x0201: parts->water_heat_close(); break;

        case 0x0300: parts->valve_open(1); break;
        case 0x0301: parts->valve_close(1); break;

        case 0x0400: parts->valve_open(2); break;
        case 0x0401: parts->valve_close(2); break;

        case 0x0500: parts->valve_open(3); break;
        case 0x0501: parts->valve_close(3); break;

        case 0x0600: parts->valve_open(4); break;
        case 0x0601: parts->valve_close(4); break;

        case 0x0700: parts->steam_pump_open(); break;
        case 0x0701: parts->steam_pump_close(); break;

        case 0x0800: parts->water_pump_open(); break;
        case 0x0801: parts->water_pump_close(); break;
        
        case 0x0900: parts->outlet_pump_open(); break;
        case 0x0901: parts->outlet_pump_close(); break;
        
        default: err = ec::INVALID; break;
    }
finish:
    return err;
}

static Error on_door_motor_ctrl(const Message &msg) {
    uint8_t ctrl;
    Error err = msg.parse_req(ctrl);
    if (err) {
        goto finish;
    }
    switch (ctrl) {
        case 0: err = doorMotor->open(); break;
        case 1: err = doorMotor->close(); break;
        default: err = ec::INVALID; break;
    }
finish:
    return err;
}

static Error on_nozzle_ctrl(const Message &msg) {
    uint16_t distance;
    Error err = msg.parse_req(distance);
    if (err) {
        goto finish;
    }
    err = nozzleMotor->run(distance);
finish:
    return err;
}

static void dispatch(const Message &msg) {

    Error err;
    switch (msg.req_cmd()) {

        case req_res::DOOR_CTRL:
            err = on_door_motor_ctrl(msg);
        break;

        case req_res::NOZZLE_CTRL:
            err = on_nozzle_ctrl(msg);
        break;

        case req_res::PARTS_CTRL:
            err = on_parts_ctrl(msg);
        break;

        case req_res::DRAW_HEAT_CTRL:
            err = on_draw_heat_ctrl(msg);
        break;

        case req_res::DRAW_WATER_TEST:  
            err = on_draw_water_test(msg);
        break;

        case req_res::SET_STEAM_DRAW_TIMEOUT:
            err = on_set_steam_draw_timeout(msg);
        break;

        case req_res::SET_STEAM_HEAT_ARG:
            err = on_set_steam_heat_arg(msg);
        break;

        case req_res::SET_FLOW_ARG:
            err = on_set_flow_arg(msg);
        break;

        case req_res::CLEAN:
            err = on_clean(msg);
        break;

        case req_res::COOKING:
            err = on_cooking(msg);
        break;

        case req_res::RF_TRANSLATE:
            on_rf_translate(msg);
        return;

        case req_res::OTA_START:    
            ota_start(msg);
        return;

        case req_res::OTA_TRANSLATE:
            ota_translate(msg);
        return;

        case req_res::OTA_COMPLETE:
            ota_complete(msg);
        return;

        case req_res::SET_PUMP_ARG:
            on_set_pump(msg);
        break;

        case req_res::SET_DOOR_ADC:
            on_set_door_adc(msg);
        break;

        default:
            err = ec::CONNOT_CMD;
        break;
    }

    cmdUart->write_resp(msg.seq(), msg.req_cmd(), err);
}

void HandlerTask::run() {

    Message msg;

    for (;;) {
        m_mq.poll(msg);
        dispatch(msg);
    }   
}

}
